package com.lemon.cloud.lock;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;

import java.util.concurrent.TimeUnit;

/**
 * @author lemon
 * @version 1.0
 * @description: 分布式锁接口
 * @date Create by lemon on 2019-09-01 09:47
 */
@Slf4j
public abstract class AbstractDistributedLock implements DistributedLock, InitializingBean {
    /**
     * @param key key值
     * @return boolean 成功/失败
     * @description 获取锁: 如果锁定不可用，则当前线程将被禁用以进行线程调度，并且在获取锁之前处于休眠状态。
     * @author lemon
     * @date 2019-09-02 12:55
     */
    @Override
    public boolean lock(String key) {
        return this.lock(key, DEFAULT_LOCK_EXPIRE_TIME, DEFAULT_LOCK_EXPIRE_TIME_UNIT, DEFAULT_RETRY_TIMES,
                DEFAULT_RETRY_INTERVAL, DEFAULT_RETRY_INTERVAL_TIME_UNIT);
    }

    /**
     * @param key        key值
     * @param retryTimes 重试次数
     * @return void
     * @description 获取锁: 重试retryTimes次，如果锁定不可用，则当前线程将被禁用以进行线程调度，并且在获取锁之前处于休眠状态。
     * @author lemon
     * @date 2019-09-02 13:06
     */
    @Override
    public boolean lock(String key, int retryTimes) {
        return this.lock(key, DEFAULT_LOCK_EXPIRE_TIME, DEFAULT_LOCK_EXPIRE_TIME_UNIT, retryTimes,
                DEFAULT_RETRY_INTERVAL, DEFAULT_RETRY_INTERVAL_TIME_UNIT);
    }

    /**
     * @param key           key值
     * @param retryTimes    重试次数
     * @param retryInterval 获取锁失败的重试间隔
     * @return void
     * @description 获取锁: 重试retryTimes次，每次间隔retryInterval时间，如果锁定不可用，则当前线程将被禁用以进行线程调度，并且在获取锁之前处于休眠状态。
     * @author lemon
     * @date 2019-09-02 13:07
     */
    @Override
    public boolean lock(String key, int retryTimes, long retryInterval) {
        return this.lock(key, DEFAULT_LOCK_EXPIRE_TIME, DEFAULT_LOCK_EXPIRE_TIME_UNIT, retryTimes, retryInterval,
                DEFAULT_RETRY_INTERVAL_TIME_UNIT);
    }

    /**
     * @param key               key值
     * @param retryTimes        重试次数
     * @param retryInterval     获取锁失败重试间隔时间
     * @param retryIntervalUnit 获取锁失败重试间隔时间单位
     * @return void
     * @description 获取锁: 重试retryTimes次，每次间隔retryInterval时间，如果锁定不可用，则当前线程将被禁用以进行线程调度，并且在获取锁之前处于休眠状态。
     * @author lemon
     * @date 2019-09-02 13:07
     */
    @Override
    public boolean lock(String key, int retryTimes, long retryInterval, TimeUnit retryIntervalUnit) {
        return this.lock(key, DEFAULT_LOCK_EXPIRE_TIME, DEFAULT_LOCK_EXPIRE_TIME_UNIT, retryTimes, retryInterval, retryIntervalUnit);
    }

    /**
     * @param key        key值
     * @param expireTime 获取锁超时时间
     * @return void
     * @description 获取锁: 超时时间expireTime，如果锁定不可用，则当前线程将被禁用以进行线程调度，并且在获取锁之前处于休眠状态。
     * @author lemon
     * @date 2019-09-02 13:08
     */
    @Override
    public boolean lock(String key, long expireTime) {
        return this.lock(key, DEFAULT_LOCK_EXPIRE_TIME, DEFAULT_LOCK_EXPIRE_TIME_UNIT, DEFAULT_RETRY_TIMES,
                DEFAULT_RETRY_INTERVAL, DEFAULT_RETRY_INTERVAL_TIME_UNIT);
    }

    /**
     * @param key            key值
     * @param expireTime     获取锁超时时间
     * @param expireTimeUnit 获取锁超时时间单位
     * @return void
     * @description 获取锁: 超时时间expireTime，如果锁定不可用，则当前线程将被禁用以进行线程调度，并且在获取锁之前处于休眠状态。
     * @author lemon
     * @date 2019-09-02 13:08
     */
    @Override
    public boolean lock(String key, long expireTime, TimeUnit expireTimeUnit) {
        return this.lock(key, expireTime, expireTimeUnit, DEFAULT_RETRY_TIMES, DEFAULT_RETRY_INTERVAL,
                DEFAULT_RETRY_INTERVAL_TIME_UNIT);
    }

    /**
     * @param key        key值
     * @param expireTime 获取锁超时时间
     * @param retryTimes 重试次数
     * @return void
     * @description 获取锁: 重试retryTimes次，每次超时时间expireTime，如果锁定不可用，则当前线程将被禁用以进行线程调度，并且在获取锁之前处于休眠状态。
     * @author lemon
     * @date 2019-09-04 12:36
     */
    @Override
    public boolean lock(String key, long expireTime, int retryTimes) {
        return this.lock(key, expireTime, DEFAULT_LOCK_EXPIRE_TIME_UNIT, retryTimes, DEFAULT_RETRY_INTERVAL,
                DEFAULT_RETRY_INTERVAL_TIME_UNIT);
    }

    /**
     * @param key            key值
     * @param expireTime     获取锁超时时间
     * @param expireTimeUnit 获取锁超时时间单位
     * @param retryTimes     重试次数
     * @param retryInterval  获取锁失败重试间隔时间
     * @return void
     * @description 获取锁: 重试retryTimes次，每次超时时间expireTime，如果锁定不可用，则当前线程将被禁用以进行线程调度，并且在获取锁之前处于休眠状态。
     * @author lemon
     * @date 2019-09-04 12:36
     */
    @Override
    public boolean lock(String key, long expireTime, TimeUnit expireTimeUnit, int retryTimes, long retryInterval) {
        return this.lock(key, expireTime, expireTimeUnit, retryTimes, retryInterval, DEFAULT_RETRY_INTERVAL_TIME_UNIT);
    }

    /**
     * @param key               key
     * @param expireTime        获取锁超时时间
     * @param expireTimeUnit    获取锁超时时间单位
     * @param retryTimes        重试次数
     * @param retryInterval     获取锁失败的重试间隔
     * @param retryIntervalUnit 获取锁失败重试间隔时间单位
     * @return boolean
     * 获取锁: 重试retryTimes次，间隔retryInterval时间重试下一次，每次超时时间expireTime，如果锁定不可用，则当前线程将被禁用以进行线程调度，并且在获取锁之前处于休眠状态。
     * @author lemon
     * @date 2019-09-08 19:55
     */
    @Override
    public boolean lock(String key, long expireTime, TimeUnit expireTimeUnit, int retryTimes, long retryInterval, TimeUnit retryIntervalUnit) {
        boolean result = acquire(key, expireTime, expireTimeUnit);

        // 如果获取锁失败，按照传入的重试次数进行重试
        while ((!result) && retryTimes-- > 0) {
            try {
                log.debug("Get distributeLock failed, retry {} times remaining", retryTimes);
                Thread.sleep(TimeUnit.MILLISECONDS.convert(expireTime, expireTimeUnit));
            } catch (InterruptedException e) {
                log.warn("Interrupted!", e);
                Thread.currentThread().interrupt();
            }

            result = this.acquire(key, expireTime, expireTimeUnit);
        }

        return result;
    }

    /**
     * @param key key值
     * @return boolean 成功/失败
     * @description 获取锁可中断: 如果锁定不可用，则当前线程将被禁用以进行线程调度，并且在获取锁之前处于休眠状态。
     * @author lemon
     * @date 2019-09-02 12:55
     */
    @Override
    public boolean lockInterruptibly(String key) throws InterruptedException {
        return this.lockInterruptibly(key, DEFAULT_LOCK_EXPIRE_TIME, DEFAULT_LOCK_EXPIRE_TIME_UNIT, DEFAULT_RETRY_TIMES,
                DEFAULT_RETRY_INTERVAL, DEFAULT_RETRY_INTERVAL_TIME_UNIT);
    }

    /**
     * @param key        key值
     * @param retryTimes 重试次数
     * @return void
     * @description 获取锁可中断: 重试retryTimes次，如果锁定不可用，则当前线程将被禁用以进行线程调度，并且在获取锁之前处于休眠状态。
     * @author lemon
     * @date 2019-09-02 13:06
     */
    @Override
    public boolean lockInterruptibly(String key, int retryTimes) throws InterruptedException {
        return this.lockInterruptibly(key, DEFAULT_LOCK_EXPIRE_TIME, DEFAULT_LOCK_EXPIRE_TIME_UNIT, retryTimes,
                DEFAULT_RETRY_INTERVAL, DEFAULT_RETRY_INTERVAL_TIME_UNIT);
    }

    /**
     * @param key           key值
     * @param retryTimes    重试次数
     * @param retryInterval 获取锁失败的重试间隔
     * @return void
     * @description 获取锁可中断: 重试retryTimes次，每次间隔retryInterval时间，如果锁定不可用，则当前线程将被禁用以进行线程调度，并且在获取锁之前处于休眠状态。
     * @author lemon
     * @date 2019-09-02 13:07
     */
    @Override
    public boolean lockInterruptibly(String key, int retryTimes, long retryInterval) throws InterruptedException {
        return this.lockInterruptibly(key, DEFAULT_LOCK_EXPIRE_TIME, DEFAULT_LOCK_EXPIRE_TIME_UNIT, retryTimes,
                retryInterval, DEFAULT_RETRY_INTERVAL_TIME_UNIT);
    }

    /**
     * @param key               key值
     * @param retryTimes        重试次数
     * @param retryInterval     获取锁失败重试间隔时间
     * @param retryIntervalUnit 获取锁失败重试间隔时间单位
     * @return void
     * @description 获取锁可中断: 重试retryTimes次，每次间隔retryInterval时间，如果锁定不可用，则当前线程将被禁用以进行线程调度，并且在获取锁之前处于休眠状态。
     * @author lemon
     * @date 2019-09-02 13:07
     */
    @Override
    public boolean lockInterruptibly(String key, int retryTimes, long retryInterval, TimeUnit retryIntervalUnit) throws InterruptedException {
        return this.lockInterruptibly(key, DEFAULT_LOCK_EXPIRE_TIME, DEFAULT_LOCK_EXPIRE_TIME_UNIT, retryTimes,
                retryInterval, retryIntervalUnit);
    }

    /**
     * @param key        key值
     * @param expireTime 获取锁超时时间
     * @return void
     * @description 获取锁可中断: 重试retryTimes次，每次间隔retryInterval时间，如果锁定不可用，则当前线程将被禁用以进行线程调度，并且在获取锁之前处于休眠状态。
     * @author lemon
     * @date 2019-09-02 13:08
     */
    @Override
    public boolean lockInterruptibly(String key, long expireTime) throws InterruptedException {
        return this.lockInterruptibly(key, expireTime, DEFAULT_LOCK_EXPIRE_TIME_UNIT, DEFAULT_RETRY_TIMES,
                DEFAULT_RETRY_INTERVAL, DEFAULT_RETRY_INTERVAL_TIME_UNIT);
    }

    /**
     * @param key            key值
     * @param expireTime     获取锁超时时间
     * @param expireTimeUnit 获取锁超时时间单位
     * @return void
     * @description 获取锁可中断: 超时时间expireTime，如果锁定不可用，则当前线程将被禁用以进行线程调度，并且在获取锁之前处于休眠状态。
     * @author lemon
     * @date 2019-09-02 13:08
     */
    @Override
    public boolean lockInterruptibly(String key, long expireTime, TimeUnit expireTimeUnit) throws InterruptedException {
        return this.lockInterruptibly(key, expireTime, expireTimeUnit, DEFAULT_RETRY_TIMES, DEFAULT_RETRY_INTERVAL,
                DEFAULT_RETRY_INTERVAL_TIME_UNIT);
    }

    /**
     * @param key            key值
     * @param expireTime     获取锁超时时间
     * @param expireTimeUnit 获取锁超时时间单位
     * @param retryTimes     重试次数
     * @return void
     * @description 获取锁可中断: 重试retryTimes次，每次超时时间expireTime，如果锁定不可用，则当前线程将被禁用以进行线程调度，并且在获取锁之前处于休眠状态。
     * @author lemon
     * @date 2019-09-04 12:49
     */
    @Override
    public boolean lockInterruptibly(String key, long expireTime, TimeUnit expireTimeUnit, int retryTimes) throws InterruptedException {
        return this.lockInterruptibly(key, expireTime, expireTimeUnit, retryTimes, DEFAULT_RETRY_INTERVAL,
                DEFAULT_RETRY_INTERVAL_TIME_UNIT);
    }

    /**
     * @param key            key
     * @param expireTime     获取锁超时时间
     * @param expireTimeUnit 获取锁超时时间单位
     * @param retryTimes     重试次数
     * @param retryInterval  获取锁失败的重试间隔
     * @return void
     * @description 获取锁可中断: 重试retryTimes次，间隔retryInterval时间重试下一次，每次超时时间expireTime，如果锁定不可用，则当前线程将被禁用以进行线程调度，并且在获取锁之前处于休眠状态。
     * @author lemon
     * @date 2019-09-04 12:47
     */
    @Override
    public boolean lockInterruptibly(String key, long expireTime, TimeUnit expireTimeUnit, int retryTimes, long retryInterval) throws InterruptedException {
        return this.lockInterruptibly(key, expireTime, expireTimeUnit, retryTimes, retryInterval,
                DEFAULT_RETRY_INTERVAL_TIME_UNIT);
    }

    /**
     * @param key               key
     * @param expireTime        获取锁超时时间
     * @param expireTimeUnit    获取锁超时时间单位
     * @param retryTimes        重试次数
     * @param retryInterval     获取锁失败的重试间隔
     * @param retryIntervalUnit 获取锁失败重试间隔时间单位
     * @return 成功/失败
     * @description 获取锁可中断: 重试retryTimes次，间隔retryInterval时间重试下一次，每次超时时间expireTime，如果锁定不可用，则当前线程将被禁用以进行线程调度，并且在获取锁之前处于休眠状态。
     * @author lemon
     */
    @Override
    public boolean lockInterruptibly(String key, long expireTime, TimeUnit expireTimeUnit, int retryTimes, long retryInterval, TimeUnit retryIntervalUnit) throws InterruptedException {
        if (Thread.interrupted()) {
            throw new InterruptedException();
        }

        boolean result = acquire(key, expireTime, expireTimeUnit);

        // 如果获取锁失败，按照传入的重试次数进行重试
        while ((!result) && retryTimes-- > 0) {
            try {
                log.debug("Get distributeLock failed, retry {} times remaining", retryTimes);
                Thread.sleep(TimeUnit.MILLISECONDS.convert(expireTime, expireTimeUnit));
            } catch (InterruptedException e) {
                log.warn("Interrupted!", e);
                throw new InterruptedException();
            }

            if (Thread.interrupted()) {
                throw new InterruptedException();
            }

            result = this.acquire(key, expireTime, expireTimeUnit);
        }

        return result;
    }

    /**
     * @param key key值
     * @return boolean
     * @description 只有在调用时它是空闲的才能获取锁。
     * @author lemon
     * @date 2019-09-02 12:53
     */
    @Override
    public boolean tryLock(String key) throws InterruptedException {
        return this.tryLock(key, DEFAULT_LOCK_EXPIRE_TIME, DEFAULT_LOCK_EXPIRE_TIME_UNIT);
    }

    /**
     * @param key            key值
     * @param expireTime
     * @param expireTimeUnit
     * @return boolean
     * @description 如果锁在给定的等待时间内是空闲的并且当前线程尚未获得，则获取锁。
     * @author lemon
     * @date 2019-09-02 12:53
     */
    @Override
    public boolean tryLock(String key, long expireTime, TimeUnit expireTimeUnit) throws InterruptedException {
        return this.acquire(key, expireTime, expireTimeUnit);
    }

    /**
     * @param key
     * @param expireTime
     * @param expireTimeUnit
     * @return boolean
     * @description
     * @author lemon
     * @date 2019-09-08 20:26
     */
    protected abstract boolean acquire(final String key, long expireTime, TimeUnit expireTimeUnit);
}
